package com.wyz.socket.core.impl.dispatcher;

import com.wyz.socket.core.base.ByteNode;
import com.wyz.socket.core.base.IoArgs;
import com.wyz.socket.core.base.frame.Frame;
import com.wyz.socket.core.base.frame.send.*;
import com.wyz.socket.core.base.packet.AbstractSendPacket;

import java.io.Closeable;
import java.io.IOException;

/**
 * read sendpacket data to Conversion Frame while sendEvent come again Frame Conversion Iogras
 */
public class AsyncPacketReader implements Closeable {
    private final IoArgs ioArgs = new IoArgs();
    private final PacketProvider provider;
    private volatile ByteNode<Frame> node;
    private volatile int nodeSize;
    private short lastIdentifier = 0;
    AsyncPacketReader(PacketProvider provider) {
        this.provider = provider;
    }
    public synchronized void cancel(AbstractSendPacket<?> packet) {
        if (nodeSize == 0) {
            return;
        }
        for (ByteNode<Frame> curr = node, before = null; curr != null; before = curr, curr = curr.next) {
            Frame item = curr.item;
            if (item instanceof AbstractSendPacketFrame) {
                AbstractSendPacketFrame sendPacketFrame = (AbstractSendPacketFrame) item;
                if (sendPacketFrame.getpacket() == packet) {
                    boolean removabort = sendPacketFrame.abort();
                    if (removabort) {
                        removeFrame(curr, before);
                        if (sendPacketFrame instanceof SendHeaderFrame) {
                            break;
                        }
                    }
                    CancelSendFrame cancelSendFrame = new CancelSendFrame(sendPacketFrame.getBodyIdentifier());
                    addFrame(cancelSendFrame);
                    provider.completeSendPacket(packet, false);
                    break;
                }
            }
        }
    }


    public boolean requestTakePacket() {
        synchronized (this) {
            if (nodeSize >= 1) {
                return true;
            }
        }
        AbstractSendPacket<?> sendPacket = provider.nextPacket();
        if (sendPacket != null) {
            short identifier = getIdentifier();
            SendHeaderFrame sendHeaderFrame = new SendHeaderFrame(identifier, sendPacket);
            addFrame(sendHeaderFrame);
        }
        synchronized (this) {
            return nodeSize >= 1;
        }
    }

    public boolean requestSendHeartbeatFrame() {
        synchronized (this){
            for (ByteNode<Frame> curr = node; curr != null; curr = curr.next) {
                Frame item = curr.item;
                if (item instanceof HeartbeatSendFrame) {
                    return false;
                }
            }
            addFrame(new HeartbeatSendFrame());
            return true ;
        }
    }

    @Override
    public synchronized void close() {
        while (node != null) {
            Frame item = node.item;
            if (item instanceof AbstractSendPacketFrame) {
                AbstractSendPacket<?> packet = ((AbstractSendPacketFrame) item).getpacket();
                provider.completeSendPacket(packet, false);
            }
            node = node.next;
        }
        nodeSize = 0;
        node = null;
    }

    public short getIdentifier() {
        short i = ++lastIdentifier;
        if (i == 255) {
            lastIdentifier = 0;
        }
        return i;
    }

    public IoArgs createIoArgsAndFillData() {
        Frame currFrame = currentFrame();
        if (currFrame == null) {
            return null;
        }
        try {
            if (currFrame.handle(this.ioArgs)) {
                Frame nextFrame = currFrame.nextFrame();
                if (nextFrame != null) {
                    addFrame(nextFrame);
                } else if (currFrame instanceof SendEntityFrame) {
                    provider.completeSendPacket(((SendEntityFrame) currFrame).getpacket(), true);
                }
                popCurrentFrame();
            }
            return this.ioArgs;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


    private synchronized Frame currentFrame() {
        if (node == null) {
            return null;
        }
        return node.item;
    }

    private synchronized void addFrame(Frame frame) {
        ByteNode<Frame> currNode = new ByteNode<>(frame);
        if (node == null) {
            node = currNode;
        } else {
            node.appendWithPriority(currNode);
        }
        nodeSize++;
    }

    private synchronized void popCurrentFrame() {
        node = node.next;
        nodeSize--;
        tryChangeNode();
    }

    private void tryChangeNode() {
        if (nodeSize == 0 || node == null) {
            requestTakePacket();
        }
    }

    private synchronized void removeFrame(ByteNode<Frame> curr, ByteNode<Frame> before) {
        if (before == null) {
            node = curr.next;
        } else {
            before.next = curr.next;
        }
        nodeSize--;
        tryChangeNode();
    }


    interface PacketProvider {

        AbstractSendPacket<?> nextPacket();

        void completeSendPacket(AbstractSendPacket<?> packet, boolean isSucceed);
    }
}
